package com.ipan.kits.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * HTTP协议工具类。
 * 
 * @author iPan
 * @version 2015-05-29
 */
public class HttpUtil {

	public static final String CONTENTTYPE_HTML = "text/html";
	public static final String CONTENTTYPE_TXT = "text/plain";
	public static final String CONTENTTYPE_XML = "text/xml";
	public static final String CONTENTTYPE_JSON = "application/json";
	public static final String HEADER_REFERER = "Referer";
	public static final String HEADER_CONTENT = "Content-Type";
	public static final String HEADER_CHARSET = "Charset";
	public static final String LINE_SEPARATOR = System.getProperty("line.separator");
	public static final String HTTP_GET = "GET";
	public static final String HTTP_POST = "POST";

	private static final int TIME_OUT = 10000; // 链接超时，毫秒；
	private static final Pattern PATTERN = Pattern.compile(".*charset=(utf-8|gbk|gb2312|iso-8859-1).*"); // 返回http头部编码匹配模式
	private static final String DEFAULT_READ_ENCODE = "utf-8"; // 默认读取输入流编码
	private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
	private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new TrustAnyHostnameVerifier();
	private static boolean debug = false; // 是否调试
	private static Logger LOG = LoggerFactory.getLogger(HttpUtil.class);

	private static SSLSocketFactory initSSLSocketFactory() {
		try {
			TrustManager[] tm = {new TrustAnyTrustManager() };
			SSLContext sslContext = SSLContext.getInstance("TLS");	// ("SSL", "TLS", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			return sslContext.getSocketFactory();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	public static boolean isDebug() {
		return debug;
	}

	public static void setDebug(boolean debug) {
		HttpUtil.debug = debug;
	}

	private HttpUtil() {
	}

	public static Map<String, String> createContentTypeHeaders(String contentType) {
		Map<String, String> headers = new HashMap<String, String>();
		headers.put(HEADER_CONTENT, contentType);
		return headers;
	}

	public static Map<String, String> getHtmlHeaders(String charset) {
		return createContentTypeHeaders(CONTENTTYPE_HTML + ";charset=" + charset);
	}

	public static Map<String, String> getXmlHeaders(String charset) {
		return createContentTypeHeaders(CONTENTTYPE_XML + ";charset=" + charset);
	}

	public static Map<String, String> getTextHeaders(String charset) {
		return createContentTypeHeaders(CONTENTTYPE_TXT + ";charset=" + charset);
	}

	public static Map<String, String> getJsonHeaders(String charset) {
		return createContentTypeHeaders(CONTENTTYPE_JSON + ";charset=" + charset);
	}

	public static Map<String, String> getDefaultHeaders(String charset) {
		return createContentTypeHeaders("application/x-www-form-urlencoded;charset=" + charset);
	}

	public static String get(String url, String charset) {
		String result = null;
		try {
			Map<String, String> headers = getDefaultHeaders(charset);
			result = httpCall(url, headers, HTTP_GET, null, false, TIME_OUT, TIME_OUT);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}

	public static String get(String url, Map<String, String> headers, int timeout) { // 旧版
		String result = null;
		try {
			result = httpCall(url, headers, HTTP_GET, null, false, timeout, timeout);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}
	public static String get(String url, Map<String, String> headers, int connTimeout, int readTimeout) { // 新版
		String result = null;
		try {
			result = httpCall(url, headers, HTTP_GET, null, false, connTimeout, readTimeout);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}

	public static String post(String url, String charset, String body) {
		String result = null;
		try {
			Map<String, String> headers = getDefaultHeaders(charset);
			result = httpCall(url, headers, HTTP_POST, body, false, TIME_OUT, TIME_OUT);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}

	public static String post(String url, Map<String, String> headers, String body, int timeout) { // 旧版
		String result = null;
		try {
			result = httpCall(url, headers, HTTP_POST, body, false, timeout, timeout);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}
	public static String post(String url, Map<String, String> headers, String body, int connTimeout, int readTimeout) { // 新版
		String result = null;
		try {
			result = httpCall(url, headers, HTTP_POST, body, false, connTimeout, readTimeout);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}

	public static String mapToUrlStr(Map<String, String> map) {
		return mapToUrlStr(map, false, null);
	}

	public static String mapToUrlStr(Map<String, String> map, boolean isEncode, String encode) {
		StringBuilder buf = new StringBuilder();
		if (map != null && map.size() > 0) {
			Iterator<String> keyIter = map.keySet().iterator();
			while (keyIter.hasNext()) {
				if (buf.length() > 0) {
					buf.append("&");
				}
				String key = keyIter.next();
				String value = map.get(key);
				if (isEncode) {
					try {
						value = URLEncoder.encode(value, encode);
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
				}
				buf.append(key).append("=").append(value);
			}
		}
		return buf.toString();
	}

	/**
	 * HTTP POST请求
	 * 
	 * @param sUrl url地址
	 * @param headers HTTP头部类型
	 * @param method HTTP方法
	 * @param body POST正文
	 * @return 返回值
	 * @throws IOException 异常
	 */
	public static String httpCall(String sUrl, Map<String, String> headers, String method, String body, boolean cacheFlg, 
			int connTimeout, int readTimeout) throws IOException {
		URL url = new URL(sUrl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		if (conn instanceof HttpsURLConnection) {
			((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory);
			((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier);
		}
		conn.setRequestMethod(method);
		conn.setUseCaches(cacheFlg);
		// 超时时间设置
		conn.setDoOutput(true);
		conn.setDoInput(true);
		conn.setConnectTimeout(connTimeout);
		conn.setReadTimeout(readTimeout);
		// 设置HTTP HEADERS
		if (headers != null) { // 默认Content-Type:
								// application/x-www-form-urlencoded
			Iterator<String> keys = headers.keySet().iterator();
			while (keys.hasNext()) {
				String key = keys.next();
				conn.setRequestProperty(key, headers.get(key));
			}
		}
		// 写入正文
		if (body != null && body.length() > 0) {
			PrintWriter out = null;
			out = new PrintWriter(conn.getOutputStream(), true);
			out.print(body);
			out.close();
		}
		// 获取所有响应头字段
		Map<String, List<String>> resultHeaders = conn.getHeaderFields();
		if (debug) {
			Iterator<String> keyIter = resultHeaders.keySet().iterator();
			while (keyIter.hasNext()) {
				String key = keyIter.next();
				System.out.println("> " + key + ": " + resultHeaders.get(key));
//				LOG.info("> {}:{}", key, resultHeaders.get(key));
			}
		}
		List<String> values = resultHeaders.get(HEADER_CONTENT);
		String charset = DEFAULT_READ_ENCODE; // 默认使用utf-8读取
		if (values != null && values.size() > 0) {
			String val = values.get(0);
			Matcher matcher = PATTERN.matcher(val.toLowerCase());
			if (matcher.matches()) {
				charset = matcher.group(1);
				if (debug) {
					System.out.println("> set charset=" + charset);
//					LOG.info("> set charset={}", charset);
				}
			}
		}
		// 读取响应信息
		BufferedReader reader = null;
		StringBuilder buf = new StringBuilder();
		boolean isSuccess = true;
		try {
			int responseCode = conn.getResponseCode();
			isSuccess = (responseCode < 400);
			if (!isSuccess) { // 对方服务器出错时候返回错误页面，也返回给当前接口；
				InputStream errIn = conn.getErrorStream();
			    if (errIn != null) {
			    	reader = new BufferedReader(new InputStreamReader(errIn, charset));
			    	LOG.info("> create ErrorStream success, responseCode={}", responseCode);
			    } else {
			    	LOG.info("> create ErrorStream error, responseCode={}", responseCode);
			    }
			} else { 
				reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
			} 
			if (reader != null) {
				String line = null;
				while ((line = reader.readLine()) != null) {
					buf.append(line).append(LINE_SEPARATOR);
				}
				if (debug) {
					System.out.println("> responseCode=" + responseCode);
					System.out.println("> body=" + buf.toString());
					if (!isSuccess) {
						LOG.info("> errResponseCode={}", responseCode);
						LOG.info("> errResponseBody={}", buf.toString());
					}
				}
			}
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			conn.disconnect();
		}
		return isSuccess ? buf.toString() : "";
	}
	
	/**
	 * https 证书管理
	 */
	private static class TrustAnyTrustManager implements X509TrustManager {
		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}
	
		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}
	
		@Override
		public X509Certificate[] getAcceptedIssuers() {
//			return new X509Certificate[] {};
			return null;
		}
	}
	/**
	 * https 域名校验
	 */
	private static class TrustAnyHostnameVerifier implements HostnameVerifier {
		@Override
		public boolean verify(String hostname, SSLSession session) {
			return true;
		}
	}

	/**
	 * 检测是否为 https 请求
	 * 
	 * nginx 代理实现 https 的场景，需要对 nginx 进行如下配置：
	 *     proxy_set_header X-Forwarded-Proto https;
	 * 或者配置:
	 *     proxy_set_header X-Forwarded-Proto $scheme;
	 */
	public static boolean isHttps(HttpServletRequest request) {
		return  "https".equalsIgnoreCase(request.getHeader("X-Forwarded-Proto"))
				||
				"https".equalsIgnoreCase(request.getScheme());
	}
	
}
